1 package edu.jiangxin.apktoolbox.main;
2
3 import edu.jiangxin.apktoolbox.Version;
4 import edu.jiangxin.apktoolbox.android.dumpsys.DumpsysPanel;
5 import edu.jiangxin.apktoolbox.convert.color.ColorPickerPanel;
6 import edu.jiangxin.apktoolbox.file.batchrename.BatchRenamePanel;
7 import edu.jiangxin.apktoolbox.file.EncodeConvertPanel;
8 import edu.jiangxin.apktoolbox.file.OsConvertPanel;
9 import edu.jiangxin.apktoolbox.file.checksum.ChecksumPanel;
10 import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel;
11 import edu.jiangxin.apktoolbox.file.duplicate.DuplicateSearchPanel;
12 import edu.jiangxin.apktoolbox.file.zhconvert.ZhConvertPanel;
13 import edu.jiangxin.apktoolbox.help.*;
14 import edu.jiangxin.apktoolbox.android.i18n.I18nAddPanel;
15 import edu.jiangxin.apktoolbox.android.i18n.I18nFindLongestPanel;
16 import edu.jiangxin.apktoolbox.android.i18n.I18nRemovePanel;
17 import edu.jiangxin.apktoolbox.android.monkey.MonkeyPanel;
18 import edu.jiangxin.apktoolbox.help.settings.SettingsPanel;
19 import edu.jiangxin.apktoolbox.pdf.finder.PdfFinderPanel;
20 import edu.jiangxin.apktoolbox.pdf.passwordremover.PdfPasswordRemoverPanel;
21 import edu.jiangxin.apktoolbox.pdf.pic2pdf.Pic2PdfPanel;
22 import edu.jiangxin.apktoolbox.pdf.stat.PdfStatPanel;
23 import edu.jiangxin.apktoolbox.reverse.*;
24 import edu.jiangxin.apktoolbox.android.screenshot.ScreenShotPanel;
25 import edu.jiangxin.apktoolbox.reverse.ApktoolPanel;
26 import edu.jiangxin.apktoolbox.swing.extend.EasyFrame;
27 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
28 import edu.jiangxin.apktoolbox.swing.extend.listener.ChangeMenuListener;
29 import edu.jiangxin.apktoolbox.swing.extend.listener.ChangeMenuToUrlListener;
30 import edu.jiangxin.apktoolbox.swing.extend.listener.IPreChangeMenuCallBack;
31 import edu.jiangxin.apktoolbox.swing.extend.plugin.ChangeMenuPreparePluginController;
32 import edu.jiangxin.apktoolbox.swing.extend.plugin.PluginPanel;
33 import edu.jiangxin.apktoolbox.swing.keeper.UiStateKeeper;
34 import edu.jiangxin.apktoolbox.utils.Utils;
35 import org.apache.commons.configuration2.Configuration;
36 import org.apache.commons.lang3.StringUtils;
37 import org.apache.logging.log4j.LogManager;
38 import org.apache.logging.log4j.Logger;
39
40 import javax.swing.*;
41 import javax.swing.border.EmptyBorder;
42 import java.awt.*;
43 import java.awt.event.*;
44 import java.io.Serial;
45 import java.lang.reflect.InvocationTargetException;
46 import java.text.MessageFormat;
47 import java.util.Locale;
48 import java.util.Objects;
49
50
51
52
53
54 public final class MainFrame extends EasyFrame {
55
56 @Serial
57 private static final long serialVersionUID = 1L;
58
59 private JPanel contentPane;
60 private EasyPanel currentEasyPanel = null;
61 private JMenuBar menuBar;
62
63 public static void main(String[] args) {
64 final Logger logger = LogManager.getLogger(MainFrame.class.getSimpleName());
65 boolean isEnvironmentOk = Utils.checkAndInitEnvironment();
66 if (!isEnvironmentOk) {
67 logger.error("Environment is not ready, exit");
68 return;
69 }
70 EventQueue.invokeLater(() -> {
71 Configuration conf = Utils.getConfiguration();
72 String lookAndFeelClassName = conf.getString("look.and.feel.class.name");
73 if (StringUtils.isEmpty(lookAndFeelClassName)) {
74 lookAndFeelClassName = "com.formdev.flatlaf.FlatDarculaLaf";
75 conf.setProperty("look.and.feel.class.name", lookAndFeelClassName);
76 }
77 try {
78 UIManager.setLookAndFeel(lookAndFeelClassName);
79 } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
80 logger.error("setLookAndFeel failed, use default instead", e);
81 }
82
83 String currentLocaleLanguage = conf.getString("locale.language");
84 if (StringUtils.isEmpty(currentLocaleLanguage)) {
85 currentLocaleLanguage = Locale.ENGLISH.getLanguage();
86 conf.setProperty("locale.language", currentLocaleLanguage);
87 }
88 Locale.setDefault(Locale.forLanguageTag(currentLocaleLanguage));
89
90 MainFrame frame = new MainFrame();
91
92 boolean isAlwaysOnTop = conf.getBoolean("always.on.top", false);
93 conf.setProperty("always.on.top", isAlwaysOnTop);
94 frame.setAlwaysOnTop(isAlwaysOnTop);
95
96 frame.setVisible(true);
97 });
98 }
99
100 public MainFrame() {
101 super();
102 initialize();
103 if (SystemTray.isSupported()) {
104 configSystemTray();
105 }
106
107 setTitle(MessageFormat.format(bundle.getString("main.title"), Version.VERSION));
108 setDefaultCloseOperation(EXIT_ON_CLOSE);
109 setMenuBar();
110 contentPane = new JPanel();
111 contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
112 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
113 contentPane.add(Box.createVerticalGlue());
114 EasyPanel initPanel = new AboutPanel();
115 initPanel.init();
116 initPanel.setBorder(BorderFactory.createTitledBorder(bundle.getString("help.about.title")));
117 contentPane.add(initPanel);
118 contentPane.add(Box.createVerticalGlue());
119 setContentPane(contentPane);
120 refreshSizeAndLocation();
121 }
122
123 private void configSystemTray() {
124 SystemTray systemTray = SystemTray.getSystemTray();
125 PopupMenu popupMenu = new PopupMenu();
126 final MenuItem show = new MenuItem("Open");
127 final MenuItem exit = new MenuItem("Close");
128 ActionListener actionListener = e -> {
129 if (e.getSource() == show) {
130 setExtendedState(NORMAL);
131 setVisible(true);
132 }
133 if (e.getSource() == exit) {
134 dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
135 }
136 };
137 exit.addActionListener(actionListener);
138 show.addActionListener(actionListener);
139 popupMenu.add(show);
140 popupMenu.add(exit);
141 TrayIcon trayIcon = new TrayIcon(image, "系统托盘", popupMenu);
142 trayIcon.setImageAutoSize(true);
143 try {
144 systemTray.add(trayIcon);
145 } catch (AWTException e) {
146 logger.error("add icon to tray failed");
147 }
148 trayIcon.addMouseListener(new MouseAdapter() {
149 @Override
150 public void mouseClicked(MouseEvent e) {
151 if (e.getClickCount() == 2) {
152 setExtendedState(NORMAL);
153 setVisible(true);
154 }
155 }
156 });
157 }
158
159 private void setMenuBar() {
160 menuBar = new JMenuBar();
161 setJMenuBar(menuBar);
162
163 createReverseMenu();
164
165 createAndroidMenu();
166
167 createPdfMenu();
168
169 createFileMenu();
170
171 createConvertMenu();
172
173 createHelpMenu();
174 }
175
176 private void createHelpMenu() {
177 JMenu helpMenu = new JMenu(bundle.getString("help.title"));
178 menuBar.add(helpMenu);
179
180 JMenuItem documentMenuItem = new JMenuItem(bundle.getString("help.document.title"));
181 documentMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_DOCUMENT));
182 helpMenu.add(documentMenuItem);
183
184 JMenuItem settingsMenuItem = new JMenuItem(bundle.getString("help.settings.title"));
185 settingsMenuItem.addActionListener(new ChangeMenuToPanelListener(SettingsPanel.class, settingsMenuItem.getText()));
186 helpMenu.add(settingsMenuItem);
187
188 JMenuItem feedbackMenuItem = new JMenuItem(bundle.getString("help.feedback.title"));
189 feedbackMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_FEEDBACK));
190 helpMenu.add(feedbackMenuItem);
191
192 JMenuItem checkUpdateMenuItem = new JMenuItem(bundle.getString("help.checkupdate.title"));
193 checkUpdateMenuItem.addActionListener(new CheckUpdateActionListener(this));
194 helpMenu.add(checkUpdateMenuItem);
195
196 JMenuItem contributeMenuItem = new JMenuItem(bundle.getString("help.contribute.title"));
197 contributeMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_CONTRIBUTE));
198 helpMenu.add(contributeMenuItem);
199
200 JMenuItem aboutMenuItem = new JMenuItem(bundle.getString("help.about.title"));
201 aboutMenuItem.addActionListener(new ChangeMenuToPanelListener(AboutPanel.class, aboutMenuItem.getText()));
202 helpMenu.add(aboutMenuItem);
203 }
204
205 private void createPdfMenu() {
206 JMenu pdfMenu = new JMenu(bundle.getString("pdf.title"));
207 menuBar.add(pdfMenu);
208
209 JMenuItem pdfStatMenuItem = new JMenuItem(bundle.getString("pdf.stat.title"));
210 pdfStatMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfStatPanel.class, pdfStatMenuItem.getText()));
211 pdfMenu.add(pdfStatMenuItem);
212
213 JMenuItem pdfFinderMenuItem = new JMenuItem(bundle.getString("pdf.finder.title"));
214 pdfFinderMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfFinderPanel.class, pdfFinderMenuItem.getText()));
215 pdfMenu.add(pdfFinderMenuItem);
216
217 JMenuItem pdfPasswordRemoverMenuItem = new JMenuItem(bundle.getString("pdf.password.remover.title"));
218 pdfPasswordRemoverMenuItem.addActionListener(new ChangeMenuToPanelListener(PdfPasswordRemoverPanel.class, pdfPasswordRemoverMenuItem.getText()));
219 pdfMenu.add(pdfPasswordRemoverMenuItem);
220
221 JMenuItem pic2PdfMenuItem = new JMenuItem(bundle.getString("pictures.to.pdf.title"));
222 pic2PdfMenuItem.addActionListener(new ChangeMenuToPanelListener(Pic2PdfPanel.class, pic2PdfMenuItem.getText()));
223 pdfMenu.add(pic2PdfMenuItem);
224 }
225
226 private void createFileMenu() {
227 JMenu fileMenu = new JMenu(bundle.getString("file.title"));
228 menuBar.add(fileMenu);
229
230 JMenuItem osConvertMenuItem = new JMenuItem(bundle.getString("file.os.convert.title"));
231 osConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(OsConvertPanel.class, osConvertMenuItem.getText()));
232 fileMenu.add(osConvertMenuItem);
233
234 JMenuItem encodeConvertMenuItem = new JMenuItem(bundle.getString("file.encode.convert.title"));
235 encodeConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(EncodeConvertPanel.class, encodeConvertMenuItem.getText()));
236 fileMenu.add(encodeConvertMenuItem);
237
238 JMenuItem zhConvertMenuItem = new JMenuItem(bundle.getString("file.zh.convert.title"));
239 zhConvertMenuItem.addActionListener(new ChangeMenuToPanelListener(ZhConvertPanel.class, zhConvertMenuItem.getText()));
240 fileMenu.add(zhConvertMenuItem);
241
242 JMenuItem duplicateFindMenuItem = new JMenuItem(bundle.getString("file.duplicate.title"));
243 duplicateFindMenuItem.addActionListener(new ChangeMenuToPanelListener(DuplicateSearchPanel.class, duplicateFindMenuItem.getText()));
244 fileMenu.add(duplicateFindMenuItem);
245
246 JMenuItem batchRenameMenuItem = new JMenuItem(bundle.getString("file.batch.rename.title"));
247 batchRenameMenuItem.addActionListener(new ChangeMenuToPanelListener(BatchRenamePanel.class, batchRenameMenuItem.getText()));
248 fileMenu.add(batchRenameMenuItem);
249
250 JMenuItem checkSumMenuItem = new JMenuItem(bundle.getString("file.check.summary.title"));
251 checkSumMenuItem.addActionListener(new ChangeMenuToPanelListener(ChecksumPanel.class, checkSumMenuItem.getText()));
252 fileMenu.add(checkSumMenuItem);
253
254 JMenuItem recoveryMenuItem = new JMenuItem(bundle.getString("file.password.recovery.title"));
255 recoveryMenuItem.addActionListener(new ChangeMenuToPanelListener(RecoveryPanel.class, recoveryMenuItem.getText()));
256 fileMenu.add(recoveryMenuItem);
257 }
258
259 private void createConvertMenu() {
260 JMenu othersMenu = new JMenu(bundle.getString("others.title"));
261 menuBar.add(othersMenu);
262
263 JMenuItem colorPickerMenuItem = new JMenuItem(bundle.getString("picker.color.title"));
264 colorPickerMenuItem.addActionListener(new ChangeMenuToPanelListener(ColorPickerPanel.class, colorPickerMenuItem.getText()));
265 othersMenu.add(colorPickerMenuItem);
266 }
267
268 private void createAndroidMenu() {
269 JMenu androidMenu = new JMenu(bundle.getString("android.title"));
270 menuBar.add(androidMenu);
271
272 JMenuItem i18nAddMenuItem = new JMenuItem(bundle.getString("android.i18n.add.title"));
273 i18nAddMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nAddPanel.class, i18nAddMenuItem.getText()));
274 androidMenu.add(i18nAddMenuItem);
275
276 JMenuItem i18nFindLongestMenuItem = new JMenuItem(bundle.getString("android.i18n.longest.title"));
277 i18nFindLongestMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nFindLongestPanel.class, i18nFindLongestMenuItem.getText()));
278 androidMenu.add(i18nFindLongestMenuItem);
279
280 JMenuItem i18nRemoveMenuItem = new JMenuItem(bundle.getString("android.i18n.remove.title"));
281 i18nRemoveMenuItem.addActionListener(new ChangeMenuToPanelListener(I18nRemovePanel.class, i18nRemoveMenuItem.getText()));
282 androidMenu.add(i18nRemoveMenuItem);
283
284 JMenuItem screenShotMenuItem = new JMenuItem(bundle.getString("android.screenshot.title"));
285 screenShotMenuItem.addActionListener(new ChangeMenuToPanelListener(ScreenShotPanel.class, screenShotMenuItem.getText()));
286 screenShotMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
287 androidMenu.add(screenShotMenuItem);
288
289 JMenuItem monkeyMenuItem = new JMenuItem(bundle.getString("android.monkey.title"));
290 monkeyMenuItem.addActionListener(new ChangeMenuToPanelListener(MonkeyPanel.class, monkeyMenuItem.getText()));
291 androidMenu.add(monkeyMenuItem);
292
293 JMenuItem dumpsysMenuItem = new JMenuItem(bundle.getString("android.dumpsys.title"));
294 dumpsysMenuItem.addActionListener(new ChangeMenuToPanelListener(DumpsysPanel.class, dumpsysMenuItem.getText()));
295 androidMenu.add(dumpsysMenuItem);
296 }
297
298 private void createReverseMenu() {
299 JMenu reverseMenu = new JMenu(bundle.getString("reverse.title"));
300 reverseMenu.setMnemonic(KeyEvent.VK_R);
301 menuBar.add(reverseMenu);
302
303 JMenuItem pluginVersionMenuItem = new JMenuItem(bundle.getString("reverse.plugin.version.title"));
304 pluginVersionMenuItem.addActionListener(new ChangeMenuToUrlListener(Constant.URL_PLUGIN_VERSION));
305 reverseMenu.add(pluginVersionMenuItem);
306
307 JMenuItem apktoolMenuItem = new JMenuItem(bundle.getString("reverse.apktool.title"), KeyEvent.VK_D);
308 apktoolMenuItem.addActionListener(new ChangeMenuToPanelListener(ApktoolPanel.class, apktoolMenuItem.getText()));
309 reverseMenu.add(apktoolMenuItem);
310
311 JMenuItem apkSignMenuItem = new JMenuItem(bundle.getString("reverse.apksigner.title"));
312 apkSignMenuItem.addActionListener(new ChangeMenuToPanelListener(ApkSignerPanel.class, apkSignMenuItem.getText()));
313 reverseMenu.add(apkSignMenuItem);
314
315 JMenuItem jDMenuItem = new JMenuItem(bundle.getString("reverse.jd.gui.title"));
316 jDMenuItem.addActionListener(new ChangeMenToPluginJdListener());
317 reverseMenu.add(jDMenuItem);
318
319 JMenuItem bytecodeViewerMenuItem = new JMenuItem(bundle.getString("reverse.bytecode.viewer.title"));
320 bytecodeViewerMenuItem.addActionListener(new ChangeMenuToPluginBytecodeViewerListener());
321 reverseMenu.add(bytecodeViewerMenuItem);
322
323 JMenuItem luytenMenuItem = new JMenuItem(bundle.getString("reverse.luyten.title"));
324 luytenMenuItem.addActionListener(new ChangeMenuToPluginLuytenListener());
325 reverseMenu.add(luytenMenuItem);
326
327 JMenuItem jdDuoMenuItem = new JMenuItem(bundle.getString("reverse.jd.duo.title"));
328 jdDuoMenuItem.addActionListener(new ChangeMenuToPluginJdDuoListener());
329 reverseMenu.add(jdDuoMenuItem);
330
331 JMenuItem jdaMenuItem = new JMenuItem(bundle.getString("reverse.jda.title"));
332 jdaMenuItem.addActionListener(new ChangeMenuToPluginJdaListener());
333 reverseMenu.add(jdaMenuItem);
334
335 JMenuItem jADXMenuItem = new JMenuItem(bundle.getString("reverse.jadx.title"));
336 jADXMenuItem.addActionListener(new ChangeMenuToPluginJadxListener());
337 reverseMenu.add(jADXMenuItem);
338
339 JMenuItem aXMLPrinter = new JMenuItem(bundle.getString("reverse.axmlprinter.title"));
340 aXMLPrinter.addActionListener(new ChangeMenuToPanelListener(AxmlPrinterPanel.class, aXMLPrinter.getText()));
341 reverseMenu.add(aXMLPrinter);
342 }
343
344 class ChangeMenuToPanelListener implements ChangeMenuListener {
345
346 Class<? extends EasyPanel> easyPanelClass;
347
348 EasyPanel panel = null;
349
350 String title;
351
352 public ChangeMenuToPanelListener(Class<? extends EasyPanel> easyPanelClass, String title) {
353 this.easyPanelClass = easyPanelClass;
354 this.title = title;
355 panel = createEasyPanel();
356 }
357
358 @Override
359 public boolean isNeedPreChangeMenu() {
360 return panel.isNeedPreChangeMenu();
361 }
362
363 @Override
364 public void onPreChangeMenu(IPreChangeMenuCallBack callBack) {
365 if (panel instanceof PluginPanel pluginPanel) {
366 pluginPanel.preparePlugin(new ChangeMenuPreparePluginController(pluginPanel.getPluginFilename(), pluginPanel.isPluginNeedUnzip(), pluginPanel.isPluginNeedUnzipToSeparateDir(), callBack));
367 }
368 }
369
370 @Override
371 public void onChangeMenu() {
372 UiStateKeeper.save(currentEasyPanel);
373 contentPane.removeAll();
374 contentPane.add(Box.createVerticalGlue());
375 panel.init();
376 panel.setBorder(BorderFactory.createTitledBorder(title));
377 contentPane.add(panel);
378 logger.info("Panel changed: " + panel.getClass().getSimpleName());
379 contentPane.add(Box.createVerticalGlue());
380 contentPane.revalidate();
381 contentPane.repaint();
382 refreshSizeAndLocation();
383 panel.afterPainted();
384 UiStateKeeper.restore(panel);
385 currentEasyPanel = panel;
386 }
387
388 private EasyPanel createEasyPanel() {
389 if (easyPanelClass == null) {
390 return new EasyPanel();
391 }
392 EasyPanel retEasyPanel = null;
393 try {
394 retEasyPanel = easyPanelClass.getDeclaredConstructor().newInstance();
395 } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
396 NoSuchMethodException e) {
397 logger.info("createEasyPanel failed: {}", e.getMessage());
398 }
399 return Objects.requireNonNullElseGet(retEasyPanel, EasyPanel::new);
400 }
401 }
402
403 @Override
404 protected void onWindowClosing(WindowEvent e) {
405 super.onWindowClosing(e);
406 UiStateKeeper.save(currentEasyPanel);
407 }
408
409 @Override
410 protected void onWindowIconified(WindowEvent e) {
411 super.onWindowIconified(e);
412 UiStateKeeper.save(currentEasyPanel);
413 }
414 }